/**
 * 文件：GamesLibraryService.java
 * 作者：赵子樵
 * 完成度：完成
 * 最后修改日期：2023-7-2 21:48
 */

package com.gomokult.service;

import java.sql.Timestamp;
import java.util.List;
import com.gomokult.entity.Game;
import com.gomokult.entity.GameDisplayInfo;
import com.gomokult.exception.DuplicatedOperationException;
import com.gomokult.exception.IllegalOperationException;
import com.gomokult.exception.InvalidIDException;
import com.gomokult.exception.NoSuchPageException;

/**
 * 提供与对局信息库相关的服务接口。
 * @author 赵子樵
 */
public interface GamesLibraryService {
	/**
	 * 根据对局ID获取其对应的{@code Game}实体。
	 * @param gameID 需要查询的对局的ID字段
	 * @return ID为{@code gameID}的持久化对象
	 * @throws InvalidIDException 当ID无效或不存在时
	 */
	Game getGame(int gameID) throws InvalidIDException;

	/**
	 * 向历史对局库中提交对局数据。
	 * @param gameData 需要提交到数据库的对局数据。需要注意的是：该{@code Game}对象只是作为提交数据的载体，其中的{@code gameID}字段将被忽略，可以填写为任意值，真正的主键由数据库负责生成维护
	 * @return 一个整数，表示数据库为该对局分配的ID（即主键）
	 */
	int submitGame(Game gameData);

	/**
	 * 将用户{@code userID}对对局{@code gameID}的点赞状态设置为{@code liked}。
	 * @param userID 执行操作的用户ID
	 * @param gameID 目标对局ID
	 * @param liked 一个布尔值，表示用户是否点赞目标对局
	 * @return 一个布尔值，表示该方法是否对评价造成有效的改动。例如当原本的点赞状态与{@code liked}参数一致时，表明执行该方法后没有发生任何变化，返回{@code false}
	 * @throws InvalidIDException 当ID无效或不存在时
	 */
	boolean setLikeState(int userID, int gameID, boolean liked) throws InvalidIDException;

	/**
	 * 获取用户{@code userID}对对局{@code gameID}的点赞状态。
	 * @param userID 待查询的用户ID
	 * @param gameID 待查询的对局ID
	 * @return 一个布尔值，表示用户是否点赞目标对局
	 * @throws InvalidIDException 当ID无效或不存在时
	 */
	boolean getLikeState(int userID, int gameID) throws InvalidIDException;

	/**
	 * 以用户{@code userID}的身份点赞/取消点赞对局{@code gameID}；该方法可以在点赞与不表态之间切换状态，其作用相当于点击一次“赞”按钮。
	 * @param userID 执行操作的用户ID
	 * @param gameID 目标对局ID
	 * @return 一个布尔值，表示执行该方法后目标对局的点赞状态
	 * @throws InvalidIDException 当ID无效或不存在时
	 */
	boolean switchLikeState(int userID, int gameID) throws InvalidIDException;

	/**
	 * 获取对局{@code gameID}的总获赞数。
	 * @param gameID 目标对局ID
	 * @return 一个整数，表示该对局的点赞数量
	 * @throws InvalidIDException 当ID无效或不存在时
	 */
	int getLikesCount(int gameID) throws InvalidIDException;

	/**
	 * 获取当前历史对局库中所有对局的数量。
	 * @return 一个整数，表示库中对局的总数																																																																						（悄悄在这里埋个雷，看看小哲群有没有偷我的注释(●'◡'●)）
	 */
	int getGamesCount();
	
	/**
	 * 获取当前历史对局库中对局的总页数，每页最多显示二十盘对局。
	 * @return 一个整数，表示对局库的总页数。特别的，当库中对局数量为0时，返回页面数量为{@code 1}
	 */
	int getGamePagesCount();

	/**
	 * 在给定的筛选条件下，获取当前历史对局库中满足条件的对局的总页数，每页最多显示二十盘对局。
	 * @param email 用于筛选特定用户的对局。如果值为空字符串{@code ""}，表示不对对局参与者进行筛选，获取所有用户的对局；否则，该方法只返回用户{@code userID}参与的对局
	 * @param startDate 用于筛选特定时间段内的对局，表示时间区间（闭区间）的起点
	 * @param endDate 用于筛选特定时间段内的对局，表示时间区间（闭区间）的终点
	 * @return 一个整数，表示对局库的总页数。特别的，当库中对局数量为0时，返回页面数量为{@code 1}
	 */
	int getGamePagesCount(String email, Timestamp startDate, Timestamp endDate);

	/**
	 * 在给定的筛选条件下，获取对局库中第{@code page}页的所有对局，每页最多显示二十盘对局。
	 * @param page 表示待查询的对局库页号，以1为起始页号
	 * @param email 用于筛选特定用户的对局。如果值为空字符串{@code ""}，表示不对对局参与者进行筛选，获取所有用户的对局；否则，该方法只返回用户{@code userID}参与的对局
	 * @param startDate 用于筛选特定时间段内的对局，表示时间区间（闭区间）的起点
	 * @param endDate 用于筛选特定时间段内的对局，表示时间区间（闭区间）的终点
	 * @return 该页对局的有序列表。对局的优先级与对局日期和获赞数量有关：对局日期越近、获赞越多，其优先级越高，具体的权重分配由实现者自行决定，结果合理即可
	 * @throws NoSuchPageException 当不存在页号为{@code page}的页面时
	 */
	List<Game> getGamesFromPage(int page, String email, Timestamp startDate, Timestamp endDate) throws NoSuchPageException;

	/**
	 * 在给定的筛选条件下，获取对局库中第{@code page}页的所有对局，每页最多显示二十盘对局。<p>
	 * * 等价于{@code getGamesFromPage(page, 用户userID的邮箱, new Date(Long.MIN_VALUE), new Date(Long.MAX_VALUE));}
	 * @param page 表示待查询的对局库页号，以1为起始页号
	 * @param userID 用于筛选特定用户的对局。如果值为{@code 0}，表示不对对局参与者进行筛选，获取所有用户的对局；否则，该方法只返回用户{@code userID}参与的对局
	 * @return 该页对局的有序列表。对局的优先级与对局日期和获赞数量有关：对局日期越近、获赞越多，其优先级越高，具体的权重分配由实现者自行决定，结果合理即可
	 * @throws InvalidIDException 当ID无效或不存在时
	 * @throws NoSuchPageException 当不存在页号为{@code page}的页面时
	 */
	List<Game> getGamesFromPage(int page, int userID) throws InvalidIDException, NoSuchPageException;
	
	/**
	 * 在给定的筛选条件下，获取对局库中第{@code page}页的所有对局，每页最多显示二十盘对局。<p>
	 * * 等价于{@code getGamesFromPage(page, "", startDate, endDate);}
	 * @param page 表示待查询的对局库页号，以1为起始页号
	 * @param startDate 用于筛选特定时间段内的对局，表示时间区间（闭区间）的起点
	 * @param endDate 用于筛选特定时间段内的对局，表示时间区间（闭区间）的终点
	 * @return 该页对局的有序列表。对局的优先级与对局日期和获赞数量有关：对局日期越近、获赞越多，其优先级越高，具体的权重分配由实现者自行决定，结果合理即可
	 * @throws NoSuchPageException 当不存在页号为{@code page}的页面时
	 */
	List<Game> getGamesFromPage(int page, Timestamp startDate, Timestamp endDate) throws NoSuchPageException;
	
	/**
	 * 获取对局库中第{@code page}页的所有对局，每页最多显示二十盘对局。<p>
	 * * 等价于{@code getGamesFromPage(page, 0, new Date(Long.MIN_VALUE), new Date(Long.MAX_VALUE));}
	 * @param page 表示待查询的对局库页号，以1为起始页号
	 * @return 该页对局的有序列表。对局的优先级与对局日期和获赞数量有关：对局日期越近、获赞越多，其优先级越高，具体的权重分配由实现者自行决定，结果合理即可
	 * @throws NoSuchPageException 当不存在页号为{@code page}的页面时
	 */
	List<Game> getGamesFromPage(int page) throws NoSuchPageException;

	/**
	 * 获取用户{@code userID}最近十场对局，以对局开始时间倒序排列。如果该用户对局总数不足十场，返回其所有对局。<p>
	 * @param userID 需要查询的用户的ID字段
	 * @return 该用户最近对局的有序列表，以对局开始时间倒序排列
	 * @throws InvalidIDException 当ID无效或不存在时
	 */
	List<Game> getRecentGamesOfUser(int userID) throws InvalidIDException;

	/**
	 * 获取用户{@code userID}在个人主页中展示对局的总页数（每页最多显示二十盘对局）。
	 * @return 一个整数，表示目标用户对局展示页的总页数
	 */
	int getUserDisplayedGamePagesCount(int userID);
	
	/**
	 * 获取用户{@code userID}对局展示页中第{@code page}页的全部对局，每页最多显示二十盘对局。请注意：不同于历史对局库的排序规则，在对局展示页中，对局的优先级与用户将其添加到展示页的时间有关。对局被添加的时间越晚，优先级越高，最近添加的对局优先级最高。
	 * @param userID 待查询用户的ID字段
	 * @param page 待查询页号，以1为起始页号
	 * @return 该页对局的有序列表，以添加到展示页的时间倒序排列
	 * @throws InvalidIDException 当ID无效或不存在时
	 * @throws NoSuchPageException 当不存在页号为{@code page}的页面时
	 */
	List<GameDisplayInfo> getUserDisplayedGamesFromPage(int userID, int page) throws InvalidIDException, NoSuchPageException;
	
	/**
	 * 判断对局{@code gameID}是否处于用户{@code userID}的对局展示页上。
	 * @param userID 待查询的用户ID
	 * @param gameID 待查询的对局ID
	 * @return 一个布尔值，表示用户是否展示该对局
	 * @throws InvalidIDException 当ID无效或不存在时
	 */
	boolean getGameDisplayState(int userID, int gameID) throws InvalidIDException;

	/**
	 * 将对局{@code gameID}添加到用户{@code userID}的对局展示页。
	 * @param userID 目标用户ID
	 * @param gameID 需要被添加到展示页的对局ID
	 * @param description 描述字段，可用于显示用户推荐展示该对局的理由
	 * @throws InvalidIDException 当ID无效或不存在时
	 * @throws IllegalOperationException 当用户{@code userID}不是目标对局的参与者时
	 * @throws DuplicatedOperationException 当目标对局已存在于该用户的展示页中时
	 */
	void displayGame(int userID, int gameID, String description) throws InvalidIDException, IllegalOperationException, DuplicatedOperationException;

	/**
	 * 将对局{@code gameID}从用户{@code userID}的对局展示页中移除。当目标对局本就不存在于该用户的对局展示页时，该方法不造成任何影响，也不必抛出任何异常。
	 * @param userID 目标用户ID
	 * @param gameID 需要被从展示页中移除的对局ID
	 * @throws InvalidIDException 当ID无效或不存在时
	 */
	void undisplayGame(int userID, int gameID) throws InvalidIDException;

	/**
	 * 以用户{@code userID}的身份展示/取消展示对局{@code gameID}；该方法可以在展示与取消展示之间切换状态：如果原本目标对局在该用户的对局展示页中，则将该对局从展示页中删除；如果不存在，则将其添加到展示页中。
	 * @param userID 执行操作的用户ID
	 * @param gameID 目标对局ID
	 * @return 一个布尔值，表示执行该方法后目标对局是否在该用户的展示页中
	 * @throws InvalidIDException 当ID无效或不存在时
	 */
	boolean switchGameDisplayState(int userID, int gameID) throws InvalidIDException;
}
